/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <isa.h>

/* We use the POSIX regex functions to process regular expressions.
 * Type 'man regex' for more information about POSIX regex functions.
 */
#include <regex.h>
#include <memory/paddr.h>
#include "../../isa/riscv32/local-include/reg.h"

enum {
  TK_NOTYPE = 256, TK_EQ, TK_Dint, TK_NEG, TK_POINT, TK_Hex, TK_REG, TK_UEQ, TK_AND,

  /* TODO: Add more token types */

};

static struct rule {
  const char *regex;
  int token_type;
  int pre;
} rules[] = {

  /* TODO: Add more rules.
   * Pay attention to the precedence level of different rules. 
   */

  {"\\(", '(', 1},     // left bracket
  {"\\)", ')', 1},     // right bracket
  {"\\*", '*', 3},     // multi
  {"\\/", '/', 3},     // div

  {" +", TK_NOTYPE, 9},    // spaces
  {"\\+", '+' , 4},         // plus  \\转义字符
  {"\\-", '-' , 4},         // minus
  {"==", TK_EQ, 7},        // equal
  {"!=", TK_UEQ, 7},        // unequal
  {"&&", TK_AND, 8},        // and
  {"\\b0[xX][0-9a-fA-F]+\\b", TK_Hex , 9}, // 十六进制 \b匹配单词开头或结尾 0-9 匹配任何单个数字字符 a-f 匹配任何小写十六进制数字字符 A-F 匹配任何大写十六进制数字字符
  {"\\$[0-9a-z]+", TK_REG , 9},//寄存器
  {"[0-9]+", TK_Dint , 9}, // 十进制整数

  {"", TK_NEG , 2},     // 负数
  {"", TK_POINT , 2}, // 指针
};

#define NR_REGEX ARRLEN(rules)

static regex_t re[NR_REGEX] = {};

/* Rules are used for many times.
 * Therefore we compile them only once before any usage.
 */
void init_regex() {
  int i;
  char error_msg[128];
  int ret;

  for (i = 0; i < NR_REGEX; i ++) {
    ret = regcomp(&re[i], rules[i].regex, REG_EXTENDED);
    if (ret != 0) {
      regerror(ret, &re[i], error_msg, 128);
      panic("regex compilation failed: %s\n%s", error_msg, rules[i].regex);
    }
  }
}

typedef struct token {
  int type;
  char str[32];
  int pre;
} Token;

static Token tokens[2048] __attribute__((used)) = {};
static int nr_token __attribute__((used))  = 0;

static bool make_token(char *e) {
  int position = 0;
  int i;
  regmatch_t pmatch;

  nr_token = 0;

  while (e[position] != '\0') {
    /* Try all rules one by one. */
    for (i = 0; i < NR_REGEX; i ++) {
      if (regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
        char *substr_start = e + position;
        int substr_len = pmatch.rm_eo;

        Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s",
            i, rules[i].regex, position, substr_len, substr_len, substr_start);

        position += substr_len;

        /* TODO: Now a new token is recognized with rules[i]. Add codes
         * to record the token in the array `tokens'. For certain types
         * of tokens, some extra actions should be performed.
         */

        switch (rules[i].token_type) {
          case TK_NOTYPE: break;
          default:
          {
            memset(tokens[nr_token].str, '\0', sizeof(tokens[nr_token].str)); //清除内存，避免不完全覆盖

            tokens[nr_token].type = rules[i].token_type;
            tokens[nr_token].pre = rules[i].pre;
  
            strncpy(tokens[nr_token].str, substr_start , substr_len);
//            	printf("type:%d value:%s \r\n",tokens[nr_token].type,tokens[nr_token].str);
            nr_token++;
          }
        }

        break;
      }
    }

    if (i == NR_REGEX) {
      printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
      return false;
    }
  }

  return true;
}

bool check_parentheses(int p ,int q){ 
    int i,flag = 0;
    if(tokens[p].type != '(' || tokens[q].type != ')') 
    	return false; //首尾没有括号
    for(i = p ; i <= q ; i ++){    //形如(x+y)*(z+q)
    	if(tokens[i].type == '(')
    		flag++;
      else if(tokens[i].type == ')')
        flag--;
      if(flag == 0 && i < q)
        return false ;
    }
    if( flag != 0 ) //括号不匹配
    	return false;   
    return true;                   
}

//主运算符寻找函数
int find_dom(int p , int q){             
    int i ,dom = p, left_n = 0;
    int pr = 0 ;
    for(i = p ; i <= q ; i++){
    		//skip the parentheses
        if(tokens[i].type == '('){
            left_n ++;
            i++;
            while(1){
                if(tokens[i].type == '(') 
                	left_n ++;
                else if(tokens[i].type == ')') 
                	left_n --;                
                if(left_n == 0)
                	break;
                i++;
            }  
            if(i > q)
            	break;
        }
        //优先级判断 优先级低的做为主运算符   
        else if(tokens[i].type != TK_Dint && tokens[i].type != TK_Hex && tokens[i].type != TK_REG && tokens[i].pre >= pr){
        	pr = tokens[i].pre;
          dom = i;
        }
    }           
//    printf("%d\n",left_n);
		return dom;
}

word_t eval(int p, int q) {
	word_t val1,val2;
  bool success = false;
	int op;
  int n;

	//printf("p is = %d, q is = %d \r\n",p,q);
  if (p > q) {
    /* Bad expression */
    assert(0);
  }
  else if (p == q) {
    /* Single token.
     * For now this token should be a number.
     * Return the value of the number.
     */
     if(tokens[p].type == TK_Hex)
     {
     		sscanf(tokens[p].str, "%x", &n);
     		return n;
     }
     else if(tokens[p].type == TK_REG)
     {
        n = isa_reg_str2val(tokens[p].str,&success);
        if(success == false)
        {
          Log("Incorrect Reg");
          assert(0);
        }
        else
          return n;
     }
     //printf("p=q\r\n");
     //printf("tokens[%d].str = %d\r\n",p,atoi(tokens[p].str));
     return atoi(tokens[p].str);
  }
  else if (check_parentheses(p, q) == true) {
    /* The expression is surrounded by a matched pair of parentheses.
     * If that is the case, just throw away the parentheses.
     */
    //printf("get parentheses\r\n");
    return eval(p + 1, q - 1);
  }
  else {
    op = find_dom(p,q);
    //printf("op is = %c, op = %d \r\n",tokens[op].type,op);
    
    //遇到负号或指针时的处理
    if(tokens[op].type == TK_NEG)
    {
			return -eval(op + 1, q);
    }
    else if(tokens[op].type == TK_POINT)
    {
			return paddr_read(eval(op + 1, q),4);
    }
    else
    {
    	val1 = eval(p, op - 1);
    	val2 = eval(op + 1, q);
    }

    switch (tokens[op].type) {
      case '+': printf("%d+%d \r\n",val1,val2);return val1 + val2; 
      case '-': printf("%d-%d \r\n",val1,val2);return val1 - val2;
      case '*': printf("%d*%d \r\n",val1,val2);return val1 * val2;
      case '/': 
                if( val2==0 )
                {
                  Log("Unvalid Expression");
                  assert(val2!=0);           
                }
                else
                {
                  printf("%d/%d \r\n",val1,val2);
                  return val1 / val2;
                }
      case TK_EQ: return val1 == val2;
      case TK_UEQ: return val1 != val2;
      case TK_AND: return val1 && val2;
      default: assert(0);
    }
  }
}


word_t expr(char *e, bool *success) {
  if (!make_token(e)) {
    *success = false;
    return 0;
  }
  else
  	*success = true;

  /* TODO: Insert codes to evaluate the expression. */
  //判断负号和指针
	for(int i = 0; i < nr_token; i++) {
		if(tokens[i].type=='*'&&(i==0 || (tokens[i-1].type!=TK_Dint && tokens[i-1].type!=TK_Hex && tokens[i-1].type!=')') || tokens[i-1].type=='('))
		{
			tokens[i].type = TK_POINT;
			tokens[i].pre = 2;
		}
		if(tokens[i].type=='-'&&(i==0 || (tokens[i-1].type!=TK_Dint && tokens[i-1].type!=TK_Hex && tokens[i-1].type!=')') || tokens[i-1].type=='('))
		{
		  tokens[i].type = TK_NEG;
		  tokens[i].pre = 2;
		}
		//printf("type:%d value:%s pre:%d \r\n",tokens[i].type,tokens[i].str,tokens[i].pre);
	}

  return eval(0,nr_token-1);
}
